// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <turbo/threading/thread_id_name_manager.h>

#include <stdlib.h>
#include <string.h>

#include <turbo/log/logging.h>

namespace turbo {
    namespace {

        static const char kDefaultName[] = "";
        static std::string *g_default_name;

    }

    ThreadIdNameManager::ThreadIdNameManager()
            : main_process_name_(nullptr),
              main_process_id_(kInvalidThreadId) {
        g_default_name = new std::string(kDefaultName);

        std::unique_lock locked(lock_);
        name_to_interned_name_[kDefaultName] = g_default_name;
    }

    ThreadIdNameManager::~ThreadIdNameManager() {
    }

    ThreadIdNameManager *ThreadIdNameManager::GetInstance() {
        static ThreadIdNameManager ins;
        return &ins;
    }

    const char *ThreadIdNameManager::GetDefaultInternedString() {
        return g_default_name->c_str();
    }

    void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle,
                                             PlatformThreadId id) {
        std::unique_lock locked(lock_);
        thread_id_to_handle_[id] = handle;
        thread_handle_to_interned_name_[handle] =
                name_to_interned_name_[kDefaultName];
    }

    void ThreadIdNameManager::SetName(PlatformThreadId id, const char *name) {
        std::string str_name(name);

        std::unique_lock locked(lock_);
        NameToInternedNameMap::iterator iter = name_to_interned_name_.find(str_name);
        std::string *leaked_str = nullptr;
        if (iter != name_to_interned_name_.end()) {
            leaked_str = iter->second;
        } else {
            leaked_str = new std::string(str_name);
            name_to_interned_name_[str_name] = leaked_str;
        }

        ThreadIdToHandleMap::iterator id_to_handle_iter =
                thread_id_to_handle_.find(id);

        // The main thread of a process will not be created as a Thread object which
        // means there is no PlatformThreadHandler registered.
        if (id_to_handle_iter == thread_id_to_handle_.end()) {
            main_process_name_ = leaked_str;
            main_process_id_ = id;
            return;
        }
        thread_handle_to_interned_name_[id_to_handle_iter->second] = leaked_str;
    }

    const char *ThreadIdNameManager::GetName(PlatformThreadId id) {
        std::unique_lock locked(lock_);

        if (id == main_process_id_)
            return main_process_name_->c_str();

        ThreadIdToHandleMap::iterator id_to_handle_iter =
                thread_id_to_handle_.find(id);
        if (id_to_handle_iter == thread_id_to_handle_.end())
            return name_to_interned_name_[kDefaultName]->c_str();

        ThreadHandleToInternedNameMap::iterator handle_to_name_iter =
                thread_handle_to_interned_name_.find(id_to_handle_iter->second);
        return handle_to_name_iter->second->c_str();
    }

    void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle,
                                         PlatformThreadId id) {
        std::unique_lock locked(lock_);
        ThreadHandleToInternedNameMap::iterator handle_to_name_iter =
                thread_handle_to_interned_name_.find(handle);

        DKCHECK(handle_to_name_iter != thread_handle_to_interned_name_.end());
        thread_handle_to_interned_name_.erase(handle_to_name_iter);

        ThreadIdToHandleMap::iterator id_to_handle_iter =
                thread_id_to_handle_.find(id);
        DKCHECK((id_to_handle_iter != thread_id_to_handle_.end()));
        // The given |id| may have been re-used by the system. Make sure the
        // mapping points to the provided |handle| before removal.
        if (id_to_handle_iter->second != handle)
            return;

        thread_id_to_handle_.erase(id_to_handle_iter);
    }

}  // namespace turbo
